Normalerweise arbeitet ein System nicht einfach nur vor sich hin, sondern reagiert auf Ereignisse,
beispielsweise von der Peripherie.
Die Erkennung von solchen Ereignissen kann entweder durch regelmäßiges Abfragen in
unserer Software, sogar in das Polling, geschehen oder wir nutzen Unterbrechungen.
Über eine spezielle Interrupt-Leitung sorgt ein externes Ereignis dafür, dass die reguläre
Abarbeitung in unserem Prozess unterbrochen und eine spezielle Behandlungsroutine ausgeführt
wird.
Im Folgenden beleuchten wir dazu Unterbrechungen auf der x86 Architektur und wie deren Behandlung
in Stubs umgesetzt wird.
Unsere Anwendung wird beispielsweise abgearbeitet und sobald ein Unterbrechungsereignis eintritt,
wird der Zustand gesichert und in die Unterbrechungsbehandlungsroutine gewechselt.
Nach der Abarbeitung wird der vorherige Zustand wiederhergestellt und die Abarbeitung der Anwendung
transparent fortgesetzt.
Aber was ist der Zustand der Anwendung, welcher bei einer Unterbrechung auf jeden Fall gesichert
werden muss?
Mindestens natürlich die aktuelle Position des Instruktionszeigers, zu dem danach wieder
zurückgekehrt werden muss.
Diesen sichert die CPU vor dem Einsprung in die Behandlungsfunktion automatisch auf dem
Stack, zusammen mit dem Statusregister flex und historisch bedingt dem Code-Segment-Register.
Um nun aus der Behandlungsfunktion wieder zurückzukehren, muss die Instruktion IRED
bzw. bei 64-bit IRED-Q aufgerufen werden, welche die Werte vom Stack wieder in die Register
liest und somit an der vorherigen Position des Instruktionszeigers fortsetzt.
Diese Rückkehrinstruktion verwenden wir auch am Ende unserer Unterbrechungsbehandlung,
welche wir somit in Assembler schreiben.
Da wir jedoch aus guten Gründen eine Hochsprache bevorzugen, wollen wir den Assemblerteil nur
so kurz wie möglich halten.
Und fügen dort einen Aufruf zu der Hochsprachenfunktion interruptHandler ein, welche wir im Falle
von stubs in C++ implementieren.
InterruptEntry ist somit nur der Einsprungspunkt zu der eigentlichen Unterbrechungsbehandlung.
Leider funktioniert der Code nun beim Übersetzen noch nicht so wie gewünscht.
Es wird stattdessen eine fehlende Referenz mit dem Funktionsnamen gemeldet.
Wenn wir uns die übersetzte C++-Datei, in der wir diese interruptHandler-Funktion implementiert
haben, genauer anschauen, erkennen wir, dass dort nur ein seltsames Symbol namens unterstrich
z17-interrupt-handler-v existiert.
Wieso?
Nun, in C++ kann es durch Funktionsüberladung vorkommen, dass es mehrere Funktionen mit
demselben Namen, aber unterschiedlichen Parametern gibt.
Um dieses Problem zu adressieren, gibt es das Name-Mangling, in welchen eben der Parameter
mit verwurschtet wird.
In unserem Fall ist es das v am Ende, das für void kein Parameter steht.
Die 17 am Anfang steht übrigens für die Länge des Funktionsnamens, entsprechend können
hier auch Namespaces mit einbezogen werden.
Da die genaue Umwandlung nicht im C++-Standard spezifiziert wurde und compiler-spezifisch
ist, auch wenn es sich inzwischen ein De-Facto-Standard entwickelt hat, können wir mit dem Schlüsselwort
externC eine Verknüpfung als C-Symbol erzwingen, wodurch nun der Symbolname unseren Erwartungen
entspricht und von unserer Assembler-Routine aufgerufen werden kann.
Das bedeutet, unsere Behandlungsroutine funktioniert.
Nun, solange unsere Anwendung keine Register verwendet.
Von der CPU werden nur die Instruktionszeige RIP und das Statusregister RFLEX gesichert,
aber unsere Anwendung verwendet sehr wahrscheinlich General-Purpose-Register wie rax und rbx.
Presenters
Zugänglich über
Offener Zugang
Dauer
00:16:03 Min
Aufnahmedatum
2020-07-28
Hochgeladen am
2021-09-20 19:06:09
Sprache
de-DE
Unterbrechungen auf der x86 Architektur für Aufgabe 2 der Lehrveranstaltung Betriebssysteme.
Folien und Transkript zum Video.